package com.xx.Tab;

import javafx.animation.*;
import javafx.application.Platform;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Point2D;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.Separator;
import javafx.scene.control.SplitPane;
import javafx.scene.effect.ColorAdjust;
import javafx.scene.image.Image;
import javafx.scene.image.PixelReader;
import javafx.scene.image.PixelWriter;
import javafx.scene.image.WritableImage;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.util.Duration;

import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.xx.util.Util.getImageView;


public class FXBDTabPane {
    final StackPane content = new StackPane();
    private final BorderPane borderPane = new BorderPane();
    SimpleListProperty<FXBDTab> tabs = new SimpleListProperty<>(FXCollections.observableArrayList());
    final HBox tabsView = new HBox();
    private final BorderPane title = new BorderPane();
    final AnchorPane anchorPane = new AnchorPane(borderPane);
//    一个root只能存两个.存了之后就会刷新.
    SplitPane root = new SplitPane(new StackPane(anchorPane));

    final Rectangle clip = new Rectangle();
    SimpleObjectProperty<FXBDTab> showTab = new SimpleObjectProperty<>();
    final ParallelTransition parallelTransition = new ParallelTransition();
    private final double millis = 400;
    final SimpleObjectProperty<Location> titleLocationProperty = new SimpleObjectProperty<>(Location.NONE);
    final SimpleObjectProperty<Location> contentLocationProperty = new SimpleObjectProperty<>(Location.NONE);
//    在哪个tab上移动.
    static FXBDTab dragaOverTab;
//    移动的tab.
    static FXBDTab dragaTab;
    static FXBDTabPane dragaTabPane;
    static Set<SplitPane> roots = new HashSet<>();
    public FXBDTabPane() {
        init();
        initTitle();
        initDraga();
        anchorPane.setPrefSize(1500,400);
        roots.add(root);
        tabs.addListener((ob,ov,nv)->{
            if (nv.isEmpty()) {
                contentLocationProperty.set(Location.DEFAULT);
                contentLocationProperty.set(Location.NONE);
            }
        });
    }
    private void init(){
        tabsView.setBackground(new Background(new BackgroundFill(Color.TRANSPARENT,new CornerRadii(10),null)));
        borderPane.setMinSize(0,0);
        tabsView.setMinSize(0,0);
        anchorPane.setMinSize(0,0);
        borderPane.prefWidthProperty().bind(anchorPane.widthProperty());
        borderPane.prefHeightProperty().bind(anchorPane.heightProperty());
        title.prefWidthProperty().bind(anchorPane.widthProperty());
        parallelTransition.setInterpolator(Interpolator.LINEAR);
        parallelTransition.setOnFinished(event -> {
            content.getChildren().forEach(e-> e.setVisible(false));
            showTab.get().content.setVisible(true);
            clip.widthProperty().bind(showTab.get().getTitle().widthProperty());
            clip.translateXProperty().bind(showTab.get().getTitle().layoutXProperty().subtract(showTab.get().tabPane.tabsView.getPadding().getLeft()));
            showTab.get().content.requestFocus();
        });
        showTab.addListener((ob,ov,nv)->{
            nv.setShow(true);
            if (ov != null)ov.setShow(false);
        });
        tabsView.setBackground(new Background(new BackgroundFill(Color.WHITE,null, null)));
        borderPane.setBackground(new Background(new BackgroundFill(Color.WHITE,null, null)));
    }

    private void initTitle() {
        tabsView.setPadding(new Insets(10,10,0,10));
        tabsView.setMinSize(0,0);
        tabsView.prefWidthProperty().bind(title.prefWidthProperty());
        title.setCenter(tabsView);

        Rectangle line = new Rectangle();
        line.setFill(new LinearGradient(
                0.0,0.0,1.0,0.0,true, CycleMethod.NO_CYCLE,
                new Stop(0.0,new Color( 0.61, 0.93, 0.99, 1.0)),
                new Stop(0.5,new Color( 0.39, 0.78, 0.97, 1.0)),
                new Stop(1.0,new Color( 0.01, 0.33, 0.84, 1.0))));
        line.setHeight(10);
        line.widthProperty().bind(title.widthProperty());

        clip.heightProperty().bind(line.heightProperty());
        line.setClip(clip);
        clip.setArcHeight(10);
        clip.setArcWidth(10);

        VBox vBox = new VBox();
        Separator separator = new Separator();
        separator.setOrientation(Orientation.HORIZONTAL);
        separator.prefWidthProperty().bind(title.widthProperty());
        vBox.getChildren().addAll(line,separator);
        vBox.setMinSize(0,0);
        title.setBottom(vBox);

        Label label = new Label();
        label.setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
        label.setGraphic(getImageView(40,"/texture/more.png"));
        ColorAdjust colorAdjust = new ColorAdjust();
        label.setEffect(colorAdjust);
        label.hoverProperty().addListener((ob,ov,nv)->colorAdjust.setBrightness(nv?0.6:0));
        label.setPadding(new Insets(20,20,0,0));
        title.setRight(label);

        borderPane.setTop(title);
        borderPane.setCenter(content);
    }
    private void initDraga(){
        titleLocationProperty.addListener((ob,ov,nv)->{
            switch (nv){
                case LEFT:
                case RIGHT: {
                    if (dragaOverTab == null || dragaOverTab == dragaTab) return;
                    dragaTab.tabPane.tabs.remove(dragaTab);
                    dragaTab.tabPane.refreshTitle();
                    dragaOverTab.tabPane.tabs.add(dragaOverTab.tabPane.tabs.indexOf(dragaOverTab)+(nv.equals(Location.LEFT)?0
                                    :1),
                            dragaTab);
                    dragaTab.tabPane = this;
                    dragaOverTab.tabPane.refreshTitle();
                    dragaTab.setVisible(true);
                    break;
                }
                case NONE:{
                    tabs.remove(dragaTab);
                    refreshTitle();
                    dragaTab.setVisible(false);
                    if (!tabs.isEmpty()) showTab(tabs.get(0));
                }
            }
        });

        Rectangle rectangle = new Rectangle();
        rectangle.setMouseTransparent(true);
        anchorPane.getChildren().add(rectangle);
        contentLocationProperty.addListener((ob,ov,nv)->{
            rectangle.setFill(Color.rgb(216,229,245));
            Point2D point2D;
            double width;
            double height;
            rectangle.setOpacity(0.5);
            switch (nv){
                case TOP:{
                    point2D = content.localToParent(0,0);
                    height=(anchorPane.getHeight() - title.getHeight()) / 2;
                    width = anchorPane.getWidth();
                    break;}
                case RIGHT:{
                    point2D = content.localToParent(0,0).add(anchorPane.getWidth()/2,0 );
                    height = (anchorPane.getHeight() - title.getHeight());
                    width = anchorPane.getWidth()/2;
                    break;}
                case BOTTOM:{
                    point2D = content.localToParent(0,0).add(0,(anchorPane.getHeight() - title.getHeight()) / 2);
                    height = (anchorPane.getHeight() - title.getHeight()) / 2;
                    width = anchorPane.getWidth();
                    break;}
                case LEFT:{
                    point2D = content.localToParent(0,0);
                    height = (anchorPane.getHeight() - title.getHeight());
                    width = anchorPane.getWidth()/2;
                    break;}
                case CENTER:{
                    point2D = content.localToParent(0,tabs.isEmpty()?-title.getHeight():0);
                    height = (anchorPane.getHeight() - (tabs.isEmpty()?0:title.getHeight()));
                    rectangle.setOpacity(tabs.isEmpty()?1:0.5);
                    width = anchorPane.getWidth();
                    break;}
                case NONE:{
                    if (tabs.isEmpty()){
                        point2D = content.localToParent(0,-title.getHeight());
                        height = anchorPane.getHeight();
                        rectangle.setOpacity(1);
                        rectangle.setFill(Color.rgb(161,174,191));
                        width = anchorPane.getWidth();
                    }
                    else {
                        point2D = new Point2D(0,0);
                        height = 0;
                        width = 0;
                    }
                    break
                    ;}
                default:{
                    point2D = new Point2D(0,0);
                    height = 0;
                    width = 0;
                }
            }
            rectangle.setLayoutX(point2D.getX());
            rectangle.setLayoutY(point2D.getY());
            rectangle.setWidth(width);
            rectangle.setHeight(height);
        });
        content.setOnDragEntered(event -> {
            if (dragaTab == null) return;
            titleLocationProperty.set(Location.DEFAULT);
            titleLocationProperty.set(Location.NONE);
            dragaTabPane = this;
        });
        content.setOnDragOver(event -> {
            if (dragaTab == null) return;
            event.acceptTransferModes(TransferMode.MOVE);
            if (dragaTabPane.tabs.isEmpty()) {
                contentLocationProperty.set(Location.CENTER);
                return;
            }
            Point2D point2D = new Point2D(event.getSceneX(),event.getSceneY()).subtract(content.localToScene(0,0));
            if (point2D.getY() < (anchorPane.getHeight() - title.getHeight()) / 5) {
                contentLocationProperty.set(Location.TOP);
                return;
            }
            else if (point2D.getY() > (anchorPane.getHeight() - title.getHeight()) / 5 * 4) {
                contentLocationProperty.set(Location.BOTTOM);
                return;
            }

            if (point2D.getX() <anchorPane.getWidth() / 3)contentLocationProperty.set(Location.LEFT);
            else if (point2D.getX() < anchorPane.getWidth() / 3 * 2) contentLocationProperty.set(Location.CENTER);
            else contentLocationProperty.set(Location.RIGHT);
        });
//         为了判断是否为鼠标在content上释放.
        AtomicBoolean isContentMouseRelease = new AtomicBoolean(false);
        content.setOnDragDropped(event -> isContentMouseRelease.set(true));
        content.setOnDragExited(event -> {
            if (dragaTab == null || isContentMouseRelease.get()) {
                isContentMouseRelease.set(false);
                return;
            }
//            过滤掉在content上释放的事件.防止对dragTab的dragaDone事件产生影响.
            dragaTabPane = null;
            contentLocationProperty.set(Location.NONE);
        });
        AtomicBoolean isAnchorPaneMouseRelease = new AtomicBoolean(false);
        anchorPane.setOnDragDropped(event -> isAnchorPaneMouseRelease.set(true));
        anchorPane.setOnDragExited(event -> {
            if (dragaTab == null || isAnchorPaneMouseRelease.get()) {
                isAnchorPaneMouseRelease.set(false);
                return;
            }
            contentLocationProperty.set(Location.NONE);
            titleLocationProperty.set(Location.NONE);
        });

        title.setOnDragOver(event -> {
            if (dragaTab == null || !tabs.isEmpty()) return;
            event.acceptTransferModes(TransferMode.MOVE);
            contentLocationProperty.set(Location.CENTER);
            dragaTabPane = this;
        });
    }
    private WritableImage scaleImage(WritableImage image, double scale) {
        int newWidth = (int) (image.getWidth() * scale);
        int newHeight = (int) (image.getHeight() * scale);
        WritableImage newImage = new WritableImage(newWidth, newHeight);
        PixelReader reader = image.getPixelReader();
        PixelWriter writer = newImage.getPixelWriter();
        for (int x = 0; x < newWidth; x++) {
            for (int y = 0; y < newHeight; y++) {
                int oldX = (int) (x / scale);
                int oldY = (int) (y / scale);
                Color color = reader.getColor(oldX, oldY);
                writer.setColor(x, y, color);
            }
        }
        return newImage;
    }
    public void addTab(FXBDTab tab){
        tabs.add(tab);
        refreshTitle();
        content.getChildren().add(tab.content);
//        让其在布局初始化后调用.
        Platform.runLater(()-> showTab(tab));
    }
    void refreshTitle(){
        tabsView.getChildren().clear();
        tabs.forEach(e-> tabsView.getChildren().add(e.getTitle()));
    }
    void closeTab(FXBDTab tab){
        int index = tabs.indexOf(tab);
        tabs.remove(tab);
        refreshTitle();
        content.getChildren().remove(tab.content);
        if (tab == showTab.get() && tabs.size() != 0) showTab(tabs.get(0));
        if (tabs.isEmpty())check(root);
    }
    private void moveTab(int index){
        if (index < 0 || index >= tabsView.getChildren().size()) return;
    }
    void showTab(FXBDTab tab){
        if(showTab != null && showTab.get() == tab) return;
        clip.widthProperty().unbind();
        clip.translateXProperty().unbind();
        parallelTransition.stop();
        parallelTransition.getChildren().clear();
        tabsView.layout();

        KeyValue xEnd = new KeyValue(clip.translateXProperty(),tab.getStartX());
        Timeline xTimeLine = new Timeline(new KeyFrame(Duration.millis(millis),xEnd));
        KeyValue widthEnd = new KeyValue(clip.widthProperty(),tab.getWidth());
        Timeline widthTimeLine = new Timeline(new KeyFrame(Duration.millis(millis), widthEnd));
        parallelTransition.getChildren().addAll(xTimeLine,widthTimeLine);
        parallelTransition.play();
        showTab .set( tab);
    }
    public static void check(SplitPane splitPane){
        if (splitPane.getItems().size() > 1) return;
        SplitPane parent = getParent(splitPane);
        if (parent == null) return;
        parent.getItems().remove(splitPane);
        roots.remove(splitPane);
        if (parent.getItems().isEmpty()) check(parent);
    }
    private static SplitPane getParent(SplitPane splitPane){
        for (SplitPane pane : roots) if (pane.getItems().contains(splitPane)) return pane;
        return null;
    }
    public void addTab(StackPane content, String info, Image item){
        addTab(new FXBDTab(content,info,item,this));
    }
    public SplitPane getTabPane(){
        return root;
    }
    }
    enum Location {
        TOP,RIGHT,BOTTOM,LEFT,CENTER,NONE,DEFAULT
    }

